﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChessEngine.Engine
{
    internal class ZobristHasher
    {
        private static ulong[] s_typeVals = null;

        static ZobristHasher()
        {
            Random rand = new Random();

            s_typeVals = new ulong[12 * 64];

            for (int i = 0; i < s_typeVals.Length; ++i)
                s_typeVals[i] = (ulong)rand.Next();
        }

        internal static void Initialize(Board b)
        {
            ulong hash = 0;

            for (int i = 0; i < b.Squares.Length; ++i)
            {
                Square sq = b.Squares[i];

                if (sq.Piece == null)
                    continue;

                hash ^= s_typeVals[GetPieceBaseIdx(sq.Piece) + i];
            }

            b.ZobristHash = hash;
        }

        internal static void UpdateHash(Board b)
        {
            MoveContent mc = b.LastMove;

            ulong hash = b.ZobristHash;

            UpdateValue(ref hash, mc.MovingPiecePrimary, mc.PawnPromoted);

            if (mc.MovingPieceSecondary.PieceType != ChessPieceType.None)
                UpdateValue(ref hash, mc.MovingPieceSecondary, false);
            else if (mc.TakenPiece.PieceType != ChessPieceType.None)
                UpdateValue(ref hash, mc.TakenPiece);

            b.ZobristHash = hash;
        }

        private static void UpdateValue(ref ulong hash, PieceMoving mv, bool toQueen)
        {
            int baseIdx = GetPieceBaseIdx(mv);

            hash ^= s_typeVals[baseIdx + mv.SrcPosition];

            // If the pawn was promoted to queen, then we need to change the base index
            // for the new piece since it is now a queen
            if (toQueen)
                baseIdx = GetPieceBaseIdx(ChessPieceType.Queen, mv.PieceColor);

            hash ^= s_typeVals[baseIdx + mv.DstPosition];
        }
        private static void UpdateValue(ref ulong hash, PieceTaken taken)
        {
            int baseIdx = GetPieceBaseIdx(taken.PieceType, taken.PieceColor);

            hash ^= s_typeVals[baseIdx + taken.Position];
        }

        private static int GetPieceBaseIdx(Piece piece)
        {
            return GetPieceBaseIdx(piece.PieceType, piece.PieceColor);
        }
        private static int GetPieceBaseIdx(PieceMoving piece)
        {
            return GetPieceBaseIdx(piece.PieceType, piece.PieceColor);
        }
        private static int GetPieceBaseIdx(ChessPieceType type, ChessPieceColor color)
        {
            int idx = (int)type;
            if (color == ChessPieceColor.Black)
                idx += 6;

            return idx * 64;
        }
    }
}
